package com.greenline.push.service.core.task;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSON;
import com.greenline.push.core.enums.AppPacketSource;
import com.greenline.push.core.enums.Channels;
import com.greenline.push.core.enums.MsgSendType;
import com.greenline.push.core.enums.TaskStatus;
import com.greenline.push.core.enums.Tasks;
import com.greenline.push.core.pushcenter.MsgForSendManager;
import com.greenline.push.core.pushcenter.MsgRetryFullManager;
import com.greenline.push.core.pushcenter.TaskSyncManager;
import com.greenline.push.dal.pushcenter.dataobject.MsgForSendDO;
import com.greenline.push.dal.pushcenter.dataobject.MsgRetryDO;
import com.greenline.push.dal.pushcenter.dataobject.TaskSyncDO;
import com.greenline.push.service.core.exception.TaskException;
import com.greenline.push.service.core.item.AppPacket;
import com.greenline.push.service.core.item.Packet;
import com.greenline.push.service.core.send.base.AppPacketBuilder;
import com.greenline.push.service.core.tmpl.AppChannelCache;
import com.greenline.push.service.core.tmpl.AppTemplateCache;
import com.greenline.push.service.core.tmpl.AppTemplateInfo;
import com.greenline.push.service.core.user.UserManager;
import com.greenline.push.service.core.utils.Constants;
import com.greenline.push.service.core.utils.PacketConvertor;

/**
 * 
 * @Type FetchRetryFullDataSchedule
 * @Desc load FetchFullTypeToRetrySchedule表数据(单例)
 * @author shuai.zuo
 * @date 2014年10月16日
 * @Version V1.0
 */
public class FetchFullTypeToRetrySchedule extends TaskBase implements Runnable {
    @Autowired
    private TaskSyncManager taskSyncManager;
    @Autowired
    PacketConvertor convertor;
    @Autowired
    private MsgRetryFullManager msgRetryFullManager;
    @Autowired
    private MsgForSendManager msgForSendManager;
    @Resource
    private AppPacketBuilder appPacketBuilder;
    @Resource
    private UserManager userManager;
    @Autowired
    private AppTemplateCache appTemplateCache;
    @Resource
    private AppChannelCache appChannelCache;

    private TaskSyncDO task = null;
    private Tasks taskType = Tasks.FETCH_FULL_TO_RETRY;
    private Long lastRecordId = null;
    private final Integer MAX_MSG_COUNT = 100;// 全量推送.每次处理100条数据
    private static final String taskName = "{全量推送解析到msg_retry_full}";
    private String USER_SPLIT = ",";
    private String taskId = null; // 定时任务没执行一次，生成唯一的识别号
    private Integer READ_MAX_PAGE = 20;
    private Logger log = Constants.LOG_SCHEDULE;

    public void startTask() throws TaskException {
        log.info(String.format("%s--任务类型:<%s>开始启动----------", taskName, task));
        // task init from here
        ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1);
        // get period
        long period = taskSyncManager.getTaskSyncByType(taskType.getType()).getRunPeriod();
        // start task
        schedule.scheduleAtFixedRate(this, 0, period, TimeUnit.SECONDS);
        log.info(String.format("%s 任务类型:<%s>启动完成,运行周期:<%s>=========", taskName, taskType, period));
    }

    @Override
    public void run() {
        excuteTask(new TaskCallBack() {
            @Override
            public TaskInfo initTaskInfo() {
                task = taskSyncManager.getTaskSyncByType(taskType.getType());
                // get uuid
                taskId = UUID.randomUUID().toString();
                log.debug(String.format("------定时任务开始执行----taskType:%s,tasks:%s,taskId:%s", taskType.getType(), taskType, taskId));
                TaskInfo taskInfo = new TaskInfo(taskName);

                // 无任务或者任务状态为running（status=1为running），task不能启动
                if (task == null) {
                    // here ,this return will broken the task forever.
                    log.debug(taskType + "-没有找到任务,停止执行。" + taskId);
                    return null;
                } else if (task.getStatus() == TaskStatus.RUNNING.getStatus()) {
                    log.debug(taskType + "-任务碰撞，停止执行。" + taskId);
                    taskInfo.setBizResult(TaskStatus.RUNNING.getName());
                    return taskInfo;
                } else {
                    task.setStatus(TaskStatus.RUNNING.getStatus());
                    taskSyncManager.update(task);

                    taskInfo.setBizResult(TaskStatus.END.getName());
                    return taskInfo;
                }
            }

            @Override
            public TaskInfo excute(TaskInfo taskInfo) {

                if (task == null) {
                    taskInfo.setSeccusss(false);
                    taskInfo.setBizResult("失败");
                    return taskInfo;
                }
                Long startId = task.getLastRecordId();
                lastRecordId = startId;
                // get all full push data
                List<MsgForSendDO> msgList = msgForSendManager.fetchByTypeAndRecord(MsgSendType.DOMAIN.getType(), lastRecordId,
                        MAX_MSG_COUNT);

                // de serializes to packet
                for (MsgForSendDO msgForSendDO : msgList) {
                    Integer readerIndex = null;
                    // if readerIndex is null or zero ,fetch from 0.other should
                    // fetch from readerIndex+1;
                    if (msgForSendDO.getReadIndex() == null || msgForSendDO.getReadIndex() == 0) {
                        msgForSendDO.setReadIndex(1);
                        readerIndex = 1;
                    } else {
                        readerIndex = msgForSendDO.getReadIndex() + 1;
                    }

                    Packet packet = convertor.convertForSendDOToPacket(msgForSendDO);
                    List<AppTemplateInfo> appTmpls = appTemplateCache.get(packet.getTemplateId(), packet.getDomainId());
                    if (null == appTmpls) {
                        log.error(String.format("%s:<<该域下未找到对应的模板:domain:%s.数据会被直接删除", taskName, packet.getDomainId()));
                        // delete msg_for_send
                        msgForSendManager.delete(msgForSendDO.getId());
                        continue;
                    }
                    int priority = -1;
                    boolean needDelete = true;
                    for (AppTemplateInfo appTempl : appTmpls) {

                        // 优先级比上次处理的大（第一次或者前次推送失败）或者相等就调用推送
                        if (appTempl.getPriority() >= priority) {
                            // 获取用户 这个循环最多走20次,最多可以取2000个用户
                            for (int i = 0; i < READ_MAX_PAGE; i++) {
                                List<Long> userIdsList = userManager.getUserListByAppId(appTempl.getAppId(), readerIndex);
                                StringBuffer userids = new StringBuffer();
                                // build user id string
                                for (Long userid : userIdsList) {
                                    userids.append(userid + USER_SPLIT);
                                }

                                Channels channel = appChannelCache.getChannel(appTempl.getAppChannelId());
                                // build packet
                                packet.setType(MsgSendType.DOMAIN.getType());
                                // packet.setRecvUserId(userId);
                                AppPacket appPacket = null;
                                try {
                                    // render template may accurs error
                                    appPacket = appPacketBuilder.buildChannelPacket(packet, appTempl, channel);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    log.error(String.format(
                                            "fetchFulldateToRetrySchecule创建appPacket出错.(可能是模版渲染问题).参数:[%s],渠道:[%s],异常:[%s]",
                                            packet.getParam(), channel, e.getMessage()));
                                    continue;
                                }
                                // packet 创建成功入库.失败选择低优先级
                                if (appPacket == null) {
                                    continue;
                                } else {
                                    // full source means the packet from
                                    // msg_retry_full
                                    appPacket.setSource(AppPacketSource.FULL);
                                    // insert to msg_retry_full
                                    MsgRetryDO msgRetryDO = wrapPacket(appTempl, channel, appPacket, userids);
                                    // insert
                                    msgRetryFullManager.insert(msgRetryDO);
                                    // set priority
                                    priority = appTempl.getPriority();
                                }

                                if ((readerIndex < READ_MAX_PAGE + msgForSendDO.getReadIndex())
                                        && (userIdsList.size() == UserManager.DEFAULT_PAGE_SIZE)) {
                                    readerIndex++;
                                } else if (userIdsList.size() < UserManager.DEFAULT_PAGE_SIZE) {
                                    // 数据已经读完[当前 app_template 下读完,下个template
                                    // 可能还没读完]
                                    // here break the for(int i = 0; i <
                                    // READ_MAX_PAGE; i++) loops
                                    break;
                                } else {
                                    // means
                                    // readerIndex==READ_MAX_PAGE+msgForSendDO.getReadIndex()
                                    log.debug(String.format("已经读取%s页数据.执行结束", readerIndex));
                                    // update msg_for_send
                                    msgForSendDO.setReadIndex(readerIndex);
                                    msgForSendManager.update(msgForSendDO);
                                    needDelete = false;
                                    break;
                                }
                            }
                        }
                    }

                    // delete
                    if (needDelete) {
                        msgForSendManager.delete(msgForSendDO.getId());
                    }
                    lastRecordId = msgForSendDO.getId();
                }

                // if fetch data &lt max_count means lastRecordId runs to the
                // last,then should return from zero
                if (msgList.size() < MAX_MSG_COUNT) {
                    // if insert all the packets to queue,so we can set
                    // lastRecordId to zero;
                    if (msgList.size() == 0 || lastRecordId == msgList.get(msgList.size() - 1).getId()) {
                        // set
                        lastRecordId = 0l;
                    }
                }
                // update status
                task.setLastRecordId(lastRecordId);
                task.setStatus(TaskStatus.END.getStatus());
                taskSyncManager.update(task);
                log.debug(String.format("%s--%s:--任务执行结束***任务id:%s", taskName, taskType, taskId));
                // update taskinfo
                taskInfo.setSeccusss(true);
                taskInfo.setCount((long) msgList.size());
                taskInfo.setBizResult("成功");
                return taskInfo;
            }

            //
            private MsgRetryDO wrapPacket(AppTemplateInfo appTempl, Channels channel, AppPacket appPacket, StringBuffer uidString) {
                MsgRetryDO msgRetryDO = new MsgRetryDO();
                msgRetryDO.setAppChannel(channel.getType());
                msgRetryDO.setAppId(appTempl.getAppId());
                msgRetryDO.setFailCount(0);
                msgRetryDO.setMemo("{all}全量推送入库成功");// 定时任务会抓取status=失败的数据
                msgRetryDO.setFirstOfferStatus(1);
                msgRetryDO.setPacketJson(JSON.toJSONString(appPacket));
                msgRetryDO.setTemplateId(appTempl.getTemplateId());
                msgRetryDO.setUidList(uidString.toString());
                return msgRetryDO;
            }
        });
    }
}
